Skip to content

Add reasoning effort controls to web UI#84

Merged
ColeMurray merged 4 commits into
mainfrom
feat/reasoning-effort-phase2
Feb 8, 2026
Merged

Add reasoning effort controls to web UI#84
ColeMurray merged 4 commits into
mainfrom
feat/reasoning-effort-phase2

Conversation

@ColeMurray

Copy link
Copy Markdown
Owner

Summary

  • Import model definitions from @open-inspect/shared, removing duplicate local MODEL_OPTIONS from both home and session pages
  • Add reasoning effort pill buttons (high / max) alongside the model selector in both home page and session page footers
  • Auto-select default reasoning effort (max) when model changes
  • Thread reasoningEffort through WebSocket hook (sendPrompt), session creation API, and prompt API to the control plane
  • Display reasoning effort in the session sidebar metadata section (e.g., "Claude Sonnet 4.5 · Max")

Depends on #83 (Phase 1: shared models + control plane + bridge).

Test plan

  • Select a model on the home page — reasoning pills (high, max) appear with max pre-selected
  • Switch models — reasoning effort resets to the new model's default
  • Toggle reasoning pill to high — verify the value flows through session creation and prompt submission
  • Open a session — reasoning effort syncs from session state and pills reflect the current setting
  • Check session sidebar — model name displays with effort suffix (e.g., "Claude Sonnet 4.5 · Max")

@greptile-apps

greptile-apps Bot commented Feb 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Overview

Greptile Summary

This PR wires a new reasoningEffort setting through the web app: it adds pill controls next to the model selector on both the home page and session page, threads the selected effort through session creation, prompt submission (REST + WS), and displays the effort in the session sidebar metadata. It also switches the web UI to consume shared MODEL_OPTIONS and reasoning configuration from @open-inspect/shared instead of duplicating model lists locally.

Main things to fix before merging:

  • use-session-socket.ts’s sendPrompt callback uses an empty dependency array but now retries with reasoningEffort captured in a closure; this can send stale model/effort values when the user changes settings before subscription completes.
  • The home page can end up with reasoningEffort unset while pills are displayed (and then posts undefined), depending on getDefaultReasoningEffort behavior; ensure effort is always valid whenever MODEL_REASONING_CONFIG exists for the selected model.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 521 to 552
@@ -545,6 +546,7 @@ export function useSessionSocket(sessionId: string): UseSessionSocketReturn {
type: "prompt",
content,
model, // Include model for per-message model switching
reasoningEffort,
})
);
}, []);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale closure in sendPrompt

sendPrompt is declared with an empty dependency array but reads wsRef.current, subscribedRef.current, and setSessionState. This was already risky before, but the new retry path now also closes over reasoningEffort (and model). If sendPrompt is called before subscription completes, the setTimeout will re-invoke sendPrompt with whatever reasoningEffort value was captured at the time of the first call, even if the user changes it before the retry fires. Fix by adding the needed dependencies (or storing the latest args in refs and reading from those in the timeout) so retries send the current model/effort and don’t rely on stale closure state.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/web/src/hooks/use-session-socket.ts
Line: 521:552

Comment:
**Stale closure in `sendPrompt`**

`sendPrompt` is declared with an empty dependency array but reads `wsRef.current`, `subscribedRef.current`, and `setSessionState`. This was already risky before, but the new retry path now also closes over `reasoningEffort` (and `model`). If `sendPrompt` is called before subscription completes, the `setTimeout` will re-invoke `sendPrompt` with whatever `reasoningEffort` value was captured at the time of the first call, even if the user changes it before the retry fires. Fix by adding the needed dependencies (or storing the latest args in refs and reading from those in the timeout) so retries send the current model/effort and don’t rely on stale closure state.

How can I resolve this? If you propose a fix, please make it concise.

Comment thread packages/web/src/app/page.tsx Outdated
Base automatically changed from feat/reasoning-effort-phase1 to main February 8, 2026 08:57
Import model definitions from @open-inspect/shared, replacing local
MODEL_OPTIONS. Add reasoning effort pill buttons (high/max) to both
home and session pages. Thread reasoningEffort through WebSocket hook,
session creation API, and prompt API. Display effort level in the
session sidebar metadata.
@ColeMurray ColeMurray force-pushed the feat/reasoning-effort-phase2 branch from 5c1cdde to 2cb2ef5 Compare February 8, 2026 08:59
- Extract duplicated reasoning effort pill UI into shared ReasoningEffortPills component
- Replace inline setSelectedModel wrappers with named handleModelChange callbacks
- Remove redundant ?? undefined coercions (getDefaultReasoningEffort already returns undefined)
- Replace local ModelOption interface with shared ModelDisplayInfo type
Clicking the text cycles through available efforts (e.g. high → max),
keeping the footer compact as more reasoning modes are added.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds “reasoning effort” as a first-class setting in the web UI (home + session pages) and threads it through session creation and prompt submission so the control plane can apply the appropriate reasoning configuration per request.

Changes:

  • Replace duplicated in-web model lists by importing MODEL_OPTIONS (and reasoning helpers) from @open-inspect/shared.
  • Add reasoning effort pill controls (e.g., high / max) to home and session footers, defaulting on model change.
  • Plumb reasoningEffort through the WebSocket prompt message, session creation API route, and prompt API route; display it in the session sidebar metadata.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/web/src/hooks/use-session-socket.ts Adds reasoningEffort to session state typing and includes it in WS prompt messages.
packages/web/src/components/sidebar/metadata-section.tsx Displays reasoning effort next to the formatted model name in sidebar metadata.
packages/web/src/components/session-right-sidebar.tsx Threads reasoningEffort from session state into MetadataSection.
packages/web/src/components/reasoning-effort-pills.tsx New pill-button UI component for selecting reasoning effort per model.
packages/web/src/app/session/[id]/page.tsx Uses shared model options, adds reasoning pills in the footer, resets default effort on model changes, and passes effort to sendPrompt.
packages/web/src/app/page.tsx Uses shared model options, adds reasoning pills, and includes effort in session creation + initial prompt submission.
packages/web/src/app/api/sessions/route.ts Passes reasoningEffort through to the control plane on session creation.
packages/web/src/app/api/sessions/[id]/prompt/route.ts Passes reasoningEffort through to the control plane on prompt submission.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/web/src/components/reasoning-effort-pills.tsx
Comment thread packages/web/src/components/reasoning-effort-pills.tsx
- Remove reasoningEffort from warming session deps (per-message attribute,
  no need to recreate session on toggle)
- Replace unsafe `as never` cast with `as ReasoningEffort` in indexOf
- Add comment explaining implicit fallback to index 0 when effort not found
@ColeMurray ColeMurray merged commit 2607ced into main Feb 8, 2026
6 checks passed
@ColeMurray ColeMurray deleted the feat/reasoning-effort-phase2 branch February 8, 2026 09:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants